﻿namespace IBTrader.Indicator.CandleStick
{
    using IBTrader.Modules.Read;
    using IBTrader.Charts;
    using System;

    /// <summary>
    /// The Average Directional Index (ADX) measures trend strength without regard to trend direction. a.k.a. DMI.
    /// The other two indicators, Plus Directional Indicator (+DI) and Minus Directional Indicator (-DI), 
    /// complement ADX by defining trend direction. Used together, chartists can determine both the direction and strength of the trend.
    /// </summary>
    class ADX : Base, IUpDown
    {
        private readonly Prices tr = new Prices(14), pos = new Prices(14), neg = new Prices(14), dx = new Prices(14);
        private readonly int type;
        private Item last;
        public event EventHandler<Line> Up, Down;
        internal event EventHandler<Line> XUp, XDown;
        public ADX(Worker worker, int interval1 = 60, int interval2 = 14, int type = 1) : base(worker, new ChartArgs { Group = 1 }, interval1) 
        {
            base.chartArgs.Add(ChartArgs.EventType.Up, new ChartArgs { Group = 2 });
            base.chartArgs.Add(ChartArgs.EventType.Down, new ChartArgs { Group = 2 });
            this.type = type;
            tr = new Prices(interval2); 
            pos = new Prices(interval2); 
            neg = new Prices(interval2); 
            dx = new Prices(interval2);
        }
        protected override void Add(object _, Item item)
        {
            if (last == null) last = item;
            var spos = Math.Max(0, item.High - last.High);
            var sneg = Math.Max(0, last.Low - item.Low);
            var str = item.TrueRange(last);
            double diPos = 0, diNeg = 0;

            switch (type)
            {
                case 1:
                case 3:
                    {
                        pos.Add(spos);
                        neg.Add(sneg);
                        tr.Add(str);
                        diPos = NaN(100 * (tr.Sum / pos.Sum));
                        diNeg = NaN(100 * (tr.Sum / neg.Sum));
                    }
                    break;
                case 2:
                case 4:
                    {
                        var npos = SmoothBlock(pos, spos);
                        var nneg = SmoothBlock(neg, sneg);
                        var ntr = SmoothBlock(tr, str);
                        diPos = npos / ntr;
                        diNeg = nneg / ntr;
                    }
                    break;
                default: throw new NotSupportedException();
            }

            dx.Add(NaN(100 * ((type > 2 ? (diPos - diNeg) : Math.Abs(diPos - diNeg)) / (diPos + diNeg))));
            if (dx.Init)
            {
                var avg = dx.Average();
                OnHit(Up, ChartArgs.EventType.Up, item, diPos);
                OnHit(Down,ChartArgs.EventType.Down, item, diNeg);
                if (diPos > 2000) OnHit(XUp, ChartArgs.EventType.XUp, item);
                if (diNeg > 2000) OnHit(XDown, ChartArgs.EventType.XDown, item);
                //if (avg > 20 && (diPos < diNeg != lastpos < lastneg || diPos > diNeg != lastpos > lastneg))
                //{
                //    if (XPos != null && (diPos < diNeg)) XPos(sender, item);
                //    if (XNeg != null && (diPos > diNeg)) XNeg(sender, item);
                //}
                OnHit(item, avg);
            }
            last = item;
            //lastpos = diPos;
            //lastneg = diNeg;
        }
        //private float lastpos, lastneg;
        private double SmoothBlock(Prices list, double curr)
        {
            if (list.Filled == 0) return list.Add(curr);
            return list.Add(list.Last() - list.Last() / list.Filled + curr);
        }
    }
}